﻿using System;
using System.Diagnostics;
using System.Security.Cryptography;


namespace Ashwani.Cryptography.Hashing
{
    public class HashCryptographer
    {
        HashAlgorithm _key;
        public const int SaltLength = 16;
        public virtual HashAlgorithm Key
        {
            get
            {
                return _key;
            }
            set
            {
                _key = value;
            }
        }

        public HashCryptographer(HashAlgorithm key)
        {
            Key = key;
        }

        public byte[] ComputeHash(byte[] plaintext)
        {
            int x = Key.HashSize;
            if (plaintext == null) throw new ArgumentNullException("plainText");
            return ComputeHash(plaintext, null);
        }

        public bool CompareHash(byte[] plaintext, byte[] hashedtext)
        {
            if (plaintext == null) throw new ArgumentNullException("plainText");
            if (hashedtext == null) throw new ArgumentNullException("hashedText");
            if (hashedtext.Length == 0) throw new ArgumentException("Array value must be greater than zero.", "hashedText");

            bool result = false;
            byte[] hashedPlainText = null;
            byte[] salt = null;
            salt = ExtractSalt(hashedtext);

            try
            {
                hashedPlainText = ComputeHash(plaintext, salt);
                result = CryptoUtility.CompareBytes(hashedPlainText, hashedtext);
            }
            catch (Exception e)
            {
                throw;
            }
            return result;
        }
        
        private byte[] ComputeHash(byte[] plaintext, byte[] salt)
        {
            if (plaintext == null)
                throw new ArgumentNullException("plaintext");


            AddSaltToPlainText(ref salt, ref plaintext);
            byte[] hash = Key.ComputeHash(plaintext);

            AddSaltToHash(salt, ref hash);

            return hash;
        }
        private void AddSaltToPlainText(ref byte[] salt, ref byte[] plaintext)
        {
            if (salt == null)
            {
                salt = CryptoUtility.GetRandomBytes(SaltLength);
            }

            plaintext = CryptoUtility.CombineBytes(salt, plaintext);
        }

        private void AddSaltToHash(byte[] salt, ref byte[] hash)
        {

            hash = CryptoUtility.CombineBytes(salt, hash);
        }

        private byte[] ExtractSalt(byte[] hashedtext)
        {
            byte[] salt = null;
            if (hashedtext.Length > SaltLength)
            {
                salt = new byte[SaltLength];
                Buffer.BlockCopy(hashedtext, 0, salt, 0, SaltLength);
            }
            return salt;
        }
    }
}